package com.banmajio.config;

import com.baomidou.dynamic.datasource.annotation.DS;
import lombok.extern.slf4j.Slf4j;
import org.postgresql.copy.CopyIn;
import org.postgresql.copy.CopyManager;
import org.postgresql.core.BaseConnection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.BufferedWriter;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;


@DS("master")
@Component
@Slf4j
public class BatchCopyHelper {
    @Autowired
    private DataSource dataSource;


    /**
     * 批量导入数据（使用注解模板）
     */
    public <T> void batchCopyInsert(String targetTable, Class<T> entityClass,
                                    List<T> dataList) throws SQLException {
        log.info("开始批量导入数据到表：{}，数据量：{}", targetTable, dataList.size());
        // 通过注解获取字段名和模板
        AnnotationTemplateProvider<T> provider = new AnnotationTemplateProvider<>(entityClass);
        List<String> fieldNames = provider.getDatabaseFieldNames();

        // 调用原有方法
        batchCopyInsert(targetTable, fieldNames, dataList, provider);
    }


    /**
     * 批量导入数据（使用注解模板）
     */
    public <T> void batchCopyInsertByChunk(String targetTable, Class<T> entityClass,
                                    List<T> dataList,int chunkSize) throws SQLException {
        // 通过注解获取字段名和模板
        AnnotationTemplateProvider<T> provider = new AnnotationTemplateProvider<>(entityClass);
        List<String> fieldNames = provider.getDatabaseFieldNames();

        // 调用原有方法
        batchCopyInsertByChunk(targetTable, fieldNames, dataList, provider,chunkSize);
    }

    /**
     * 批量导入数据（支持大文件、高性能）
     */
    public <T> void batchCopyInsert(String targetTable, List<String> fieldNames,
                                    List<T> dataList, ValueProvider<T> valueProvider) throws SQLException {
        if (dataList == null || dataList.isEmpty()) {
            return;
        }

        // 配置参数
        final int BUFFER_SIZE = 64 * 1024; // 缓冲区大小
        final int BATCH_SIZE = 100000;     // 每批记录数

        // 存储字段最大长度限制（需要根据表结构配置）
        Map<String, Integer> fieldMaxLengths = getFieldMaxLengths(targetTable);

        String fieldNamesStr = String.join(", ", fieldNames);
        String copyCommand = "COPY " + targetTable + " (" + fieldNamesStr + ") " +
                "FROM STDIN WITH (FORMAT CSV, DELIMITER '\t', NULL '\\N')";

        try (Connection conn = dataSource.getConnection()) {
            conn.setAutoCommit(false);
            BaseConnection pgConnection = conn.unwrap(BaseConnection.class);
            CopyManager copyManager = new CopyManager(pgConnection);

            byte[] buffer = new byte[BUFFER_SIZE];
            int bufferPos = 0;
            int recordCount = 0;
            int errorCount = 0;
            List<Integer> errorIndices = new ArrayList<>();

            try {
                CopyIn copyIn = null;

                for (int i = 0; i < dataList.size(); i++) {
                    T item = dataList.get(i);
                    // 每BATCH_SIZE条记录创建一个新的COPY会话
                    if (recordCount % BATCH_SIZE == 0) {
                        if (copyIn != null) {
                            copyIn.endCopy();
                        }
                        copyIn = copyManager.copyIn(copyCommand);
                    }

                    try {
                        // 验证字段长度
                        validateFields(item, fieldNames, fieldMaxLengths, i);

                        String line = valueProvider.formatLine(item);
                        byte[] lineBytes = line.getBytes(StandardCharsets.UTF_8);

                        // 缓冲区逻辑
                        if (bufferPos + lineBytes.length + 1 > BUFFER_SIZE) {
                            copyIn.writeToCopy(buffer, 0, bufferPos);
                            bufferPos = 0;
                        }
                        System.arraycopy(lineBytes, 0, buffer, bufferPos, lineBytes.length);
                        bufferPos += lineBytes.length;
                        buffer[bufferPos++] = '\n';
                        recordCount++;
                    } catch (IllegalArgumentException e) {
                        log.error("记录 {} 验证失败: {}", i, e.getMessage());
                        errorCount++;
                        errorIndices.add(i);
                        // 重置缓冲区，跳过当前错误记录
                        bufferPos = 0;
                    }
                }

                // 写入剩余数据
                if (bufferPos > 0 && copyIn != null) {
                    copyIn.writeToCopy(buffer, 0, bufferPos);
                    copyIn.endCopy();
                }

                conn.commit();
                if (errorCount > 0) {
                    log.warn("批量导入完成，但有 {} 条记录包含超长字段，索引: {}", errorCount, errorIndices);
                }
            } catch (Exception e) {
                conn.rollback();
                throw new SQLException("COPY 导入失败", e);
            } finally {
                conn.setAutoCommit(true);
            }
        }
    }



    /**
     * 对象值提供者接口，用于自定义对象到 CSV 行的转换
     */
    public interface ValueProvider<T> {
        String formatLine(T item);
    }

    /**
     * 分批处理大数据集
     */
    public <T> void batchCopyInsertByChunk(String targetTable, List<String> fieldNames,
                                           List<T> dataList, ValueProvider<T> valueProvider,
                                           int chunkSize) throws SQLException {
        if (dataList == null || dataList.isEmpty()) {
            return;
        }

        int totalSize = dataList.size();
        int batches = (totalSize + chunkSize - 1) / chunkSize;

        for (int i = 0; i < batches; i++) {
            int fromIndex = i * chunkSize;
            int toIndex = Math.min((i + 1) * chunkSize, totalSize);

            System.out.println("处理批次 " + (i + 1) + "/" + batches +
                    " (记录: " + fromIndex + " - " + (toIndex - 1) + ")");

            List<T> batch = dataList.subList(fromIndex, toIndex);
            batchCopyInsert(targetTable, fieldNames, batch, valueProvider);
        }
    }

    /**
     * 获取表字段的最大长度限制
     * 实际实现中可以从数据库元数据或配置文件获取
     */
    private Map<String, Integer> getFieldMaxLengths(String tableName) {
        // 示例实现，实际应根据表结构动态获取
        Map<String, Integer> maxLengths = new HashMap<>();

        if ("xt_volunteer_info".equals(tableName)) {
            maxLengths.put("name", 200);               // 对应 VARCHAR(200)
            maxLengths.put("id_number", 200);         // 对应 VARCHAR(200)
            maxLengths.put("id_img", 200);            // 对应 VARCHAR(200)
            maxLengths.put("mobile", 200);            // 对应 VARCHAR(200)
            maxLengths.put("service_area", 10);       // 对应 VARCHAR(10)
            maxLengths.put("service_category", 255);  // 对应 VARCHAR(255)
            maxLengths.put("service_domain", 255);    // 对应 VARCHAR(255)
            maxLengths.put("username", 20);           // 对应 VARCHAR(20)
            maxLengths.put("password", 100);          // 对应 VARCHAR(100)
            maxLengths.put("avatar", 100);            // 对应 VARCHAR(100)
            maxLengths.put("email", 200);             // 对应 VARCHAR(200)
            maxLengths.put("nickname", 10);           // 对应 VARCHAR(10)
            maxLengths.put("creator_name", 50);       // 对应 VARCHAR(50)
            maxLengths.put("updator_name", 50);       // 对应 VARCHAR(50)
            maxLengths.put("telephone", 20);          // 对应 VARCHAR(20)
            maxLengths.put("referral", 50);           // 对应 VARCHAR(50)
            maxLengths.put("address", 500);           // 对应 VARCHAR(500)
            maxLengths.put("issuing_organization", 200); // 对应 VARCHAR(200)
            maxLengths.put("start_date", 50);         // 对应 VARCHAR(50)
            maxLengths.put("expiry_date", 50);        // 对应 VARCHAR(50)
            maxLengths.put("politics_status", 20);    // 对应 VARCHAR(20)
            maxLengths.put("first_level_service_field", 200); // 对应 VARCHAR(200)
            maxLengths.put("second_level_service_field", 200); // 对应 VARCHAR(200)
            maxLengths.put("show_service_hour", 255); // 对应 VARCHAR(默认255)
            maxLengths.put("area_path", 1000);        // 对应 VARCHAR(1000)
            maxLengths.put("wx_mini_openid", 100);    // 对应 VARCHAR(100)
            maxLengths.put("union_id", 100);          // 对应 VARCHAR(100)
            maxLengths.put("wx_official_openid", 100);// 对应 VARCHAR(100)
            maxLengths.put("place_preference", 10);   // 对应 VARCHAR(10)
            maxLengths.put("place_path", 1000);       // 对应 VARCHAR(1000)

            // 对于固定长度字段(bpchar)，添加长度限制
            maxLengths.put("nationality", 3);         // 对应 BPCHAR(3)
            maxLengths.put("unified_code", 18);       // 对应 BPCHAR(18)
            maxLengths.put("nation", 2);              // 对应 BPCHAR(2)
        }else if ("volunteer_trajectory".equals(tableName)) {
            maxLengths.put("code", 255);              // 对应 VARCHAR(默认255)
            maxLengths.put("description", 255);       // 对应 VARCHAR(默认255)
            maxLengths.put("detail", 255);            // 对应 VARCHAR(默认255)
        }else if ("youngster_info".equals(tableName)) {
            maxLengths.put("guardian_name", 100);     // 对应 VARCHAR(100)
            maxLengths.put("guardian_id_number", 200);// 对应 VARCHAR(200)
            maxLengths.put("guardian_mobile", 200);   // 对应 VARCHAR(200)
            maxLengths.put("update_year", 255);       // 对应 VARCHAR(默认255)
        }

        return maxLengths;
    }

    /**
     * 验证对象字段长度
     */
    private <T> void validateFields(T item, List<String> fieldNames, Map<String, Integer> fieldMaxLengths, int recordIndex) {
        if (item == null || fieldMaxLengths.isEmpty()) {
            return;
        }

        try {
            // 反射获取对象属性值并验证长度
            Class<?> clazz = item.getClass();
            for (String fieldName : fieldNames) {
                if (!fieldMaxLengths.containsKey(fieldName)) {
                    continue;
                }

                int maxLength = fieldMaxLengths.get(fieldName);
                String methodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);

                try {
                    Method method = clazz.getMethod(methodName);
                    Object value = method.invoke(item);

                    if (value instanceof String) {
                        String strValue = (String) value;
                        if (strValue.length() > maxLength) {
                            throw new IllegalArgumentException(
                                    String.format("字段 '%s' 长度为 %d，超过最大限制 %d",
                                            fieldName, strValue.length(), maxLength));
                        }
                    }
                } catch (NoSuchMethodException e) {
                    // 忽略没有getter方法的字段
                }
            }
        } catch (Exception e) {
            throw new IllegalArgumentException("记录 " + recordIndex + " 字段验证失败: " + e.getMessage(), e);
        }
    }
}
